#include "Multigrid.h"
#include "json.hpp"

#define exp 2.718281828

int globalInd = 0;

// 用于存放范数误差
double errorNorm[4];

// 计算误差并汇报信息
template <int dim>
void report(Grid<dim> &gData, Container2D &U, Container2D &V, Container2D &EX, int times, Real *residual, double t1, double t2)
{
    // 储存范数误差
    errorNorm[globalInd] = gData.err(V - EX, 0);

    cout << "Max-norm error: " << errorNorm[globalInd] << endl;

    // 移动全局索引
    globalInd = (globalInd + 1) % 4;

    // 汇报迭代次数
    cout << "Iteration times: " << times << endl;

    // 收敛速度
    Real rate = convergence(times, residual);
    cout << "Convergence rates: " << rate << endl;

    // 运行时间
    cout << "Lapack time: " << t1 << "s" << endl;
    cout << "Multigrid time: " << t2 << "s" << endl;
}

// 计算函数
template <int dim>
void calculate(json &inputs, MultiSolver<dim> &ms)
{
    // 读取数据的总量
    int num = inputs["Num"];

    // 逐个计算
    for (int i = 0; i < num; i++)
    {
        // 当前数据
        json nj = inputs["Data"][i];

        cout << "----------------------------------------" << endl;
        cout << "Type: " << nj["Type"] << endl;
        cout << "Restriction operator: " << nj["reType"] << endl;
        cout << "Prolongation operator: " << nj["prType"] << endl;

        cout << endl;

        int n = 32;
        int p = 4;

        for (int j = 0; j < p; j++)
        {
            cout << "n = " << n << endl;

            // 记录规模
            ms.gData.n = n;

            // 使用无穷范数
            int norm = 0;

            // 读取边界数据
            ms.read(nj);
            ms.multiSolve(norm, report);

            cout << endl;

            n *= 2;
        }

        cout << "----------------------------------------" << endl;
        cout << "Convergence rates" << endl;

        // 收敛速率
        double rate = convergence(p, errorNorm);

        cout << "max-norm: " << rate << endl;

        cout << endl;
    }
}

void testOne(json &inputs)
{
    Func<1> u = [](Point<1> p)
    {
        Real x = p[0];
        return pow(exp, x);
    };

    Func<1> ux = [](Point<1> p)
    {
        Real x = p[0];
        return pow(exp, x);
    };

    Func<1> f = [](Point<1> p)
    {
        Real x = p[0];
        return -pow(exp, x);
    };

    MultiSolver<1> ms;

    // 函数
    ms.gData.f = f;
    ms.gData.u = u;
    ms.gData.du[0] = ux;

    calculate(inputs, ms);
}

void testTwo(json &inputs)
{
    Func<1> u = [](Point<1> p)
    {
        Real x = p[0];
        return sin(x);
    };

    Func<1> ux = [](Point<1> p)
    {
        Real x = p[0];
        return cos(x);
    };

    Func<1> f = [](Point<1> p)
    {
        Real x = p[0];
        return sin(x);
    };

    MultiSolver<1> ms;

    // 函数
    ms.gData.f = f;
    ms.gData.u = u;
    ms.gData.du[0] = ux;

    calculate(inputs, ms);
}

void testThree(json &inputs)
{
    Func<1> u = [](Point<1> p)
    {
        Real x = p[0];
        return cos(x);
    };

    Func<1> ux = [](Point<1> p)
    {
        Real x = p[0];
        return -sin(x);
    };

    Func<1> f = [](Point<1> p)
    {
        Real x = p[0];
        return cos(x);
    };

    MultiSolver<1> ms;

    // 函数
    ms.gData.f = f;
    ms.gData.u = u;
    ms.gData.du[0] = ux;

    calculate(inputs, ms);
}

int main()
{
    fstream fp("inputs.json", ios::in);
    json inputs;
    fp >> inputs;

    cout << "----------------------------------------" << endl;
    cout << "u(x) = e^x" << endl;
    cout << endl;
    testOne(inputs);

    cout << "----------------------------------------" << endl;
    cout << "u(x) = sin(x)" << endl;
    cout << endl;
    testTwo(inputs);

    cout << "----------------------------------------" << endl;
    cout << "u(x) = cos(x)" << endl;
    cout << endl;
    testThree(inputs);

    return 0;
}